// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:build aix 

package runtime

import (
	"internal/abi"
	"unsafe"
)

const (
	threadStackSize = 0x100000 // OS分配的线程堆栈大小
)

// funcDescriptor是一种表示函数描述符的结构
// assembler 
type funcDescriptor struct {
	fn         uintptr
	toc        uintptr
	envPointer uintptr // Golang 
}

type mOS struct {
	waitsema uintptr // 
}

func semacreate(mp *m) {
	if mp.waitsema != 0 {
		return
	}

	var sem *semt

	// 给libc的malloc打电话，而不是malloc。这将在C堆上分配空间。我们不能在这里调用mallocgc 
	// 因为它可能会导致死锁。
	sem = (*semt)(malloc(unsafe.Sizeof(*sem)))
	if sem_init(sem, 0, 0) != 0 {
		throw("sem_init")
	}
	mp.waitsema = uintptr(unsafe.Pointer(sem))
}

// go:nosplit 
func semasleep(ns int64) int32 {
	mp := getg().m
	if ns >= 0 {
		var ts timespec

		if clock_gettime(_CLOCK_REALTIME, &ts) != 0 {
			throw("clock_gettime")
		}
		ts.tv_sec += ns / 1e9
		ts.tv_nsec += ns % 1e9
		if ts.tv_nsec >= 1e9 {
			ts.tv_sec++
			ts.tv_nsec -= 1e9
		}

		if r, err := sem_timedwait((*semt)(unsafe.Pointer(mp.waitsema)), &ts); r != 0 {
			if err == _ETIMEDOUT || err == _EAGAIN || err == _EINTR {
				return -1
			}
			println("sem_timedwait err ", err, " ts.tv_sec ", ts.tv_sec, " ts.tv_nsec ", ts.tv_nsec, " ns ", ns, " id ", mp.id)
			throw("sem_timedwait")
		}
		return 0
	}
	for {
		r1, err := sem_wait((*semt)(unsafe.Pointer(mp.waitsema)))
		if r1 == 0 {
			break
		}
		if err == _EINTR {
			continue
		}
		throw("sem_wait")
	}
	return 0
}

// go:nosplit 
func semawakeup(mp *m) {
	if sem_post((*semt)(unsafe.Pointer(mp.waitsema))) != 0 {
		throw("sem_post")
	}
}

func osinit() {
	ncpu = int32(sysconf(__SC_NPROCESSORS_ONLN))
	physPageSize = sysconf(__SC_PAGE_SIZE)
}

// newosproc0是可以在初始化运行时
// 之前调用的newosproc版本。
// 
// 此函数在初始化后使用不安全，因为它不会将M作为fnarg传递。
// 
// go:nosplit 
func newosproc0(stacksize uintptr, fn *funcDescriptor) {
	var (
		attr pthread_attr
		oset sigset
		tid  pthread
	)

	if pthread_attr_init(&attr) != 0 {
		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
		exit(1)
	}

	if pthread_attr_setstacksize(&attr, threadStackSize) != 0 {
		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
		exit(1)
	}

	if pthread_attr_setdetachstate(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
		exit(1)
	}

	// 在创建过程中禁用信号，这样新线程在禁用信号的情况下启动
	// 。这将使他们在minit。
	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
	var ret int32
	for tries := 0; tries < 20; tries++ {
		// pthread_create可以毫无原因地使用EAGAIN失败
		// 但是如果重试，它就可以了。
		ret = pthread_create(&tid, &attr, fn, nil)
		if ret != _EAGAIN {
			break
		}
		usleep(uint32(tries+1) * 1000) // 毫秒。调用
	}
	sigprocmask(_SIG_SETMASK, &oset, nil)
	if ret != 0 {
		write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
		exit(1)
	}

}

var failthreadcreate = []byte("runtime: failed to create new OS thread\n")

// 对使用
// /-buildmode=c-archive或-buildmode=c-shared生成的Go代码进行同步初始化。
// Go运行时均未初始化。
// go:nosplit 
// go:nowritebarrierrec 
func libpreinit() {
	initsig(true)
}

// Ms相关函数
func mpreinit(mp *m) {
	mp.gsignal = malg(32 * 1024) // AIX wants>=8K 
	mp.gsignal.m = mp
}

// errno地址必须通过调用_errnolibc函数来检索。
// 这将返回一个指向errno 
func miniterrno() {
	mp := getg().m
	r, _ := syscall0(&libc__Errno)
	mp.perrno = r

}

func minit() {
	miniterrno()
	minitSignals()
	getg().m.procid = uint64(pthread_self())
}

func unminit() {
	unminitSignals()
}

// 的指针，该指针是从exitm调用的，而不是从drop调用的，以撤销线程拥有的
// minit、semacreate或其他资源的效果。打完电话后不要带锁。
func mdestroy(mp *m) {
}

// tstart是汇编中定义的tstart的函数描述符。
var tstart funcDescriptor

func newosproc(mp *m) {
	var (
		attr pthread_attr
		oset sigset
		tid  pthread
	)

	if pthread_attr_init(&attr) != 0 {
		throw("pthread_attr_init")
	}

	if pthread_attr_setstacksize(&attr, threadStackSize) != 0 {
		throw("pthread_attr_getstacksize")
	}

	if pthread_attr_setdetachstate(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
		throw("pthread_attr_setdetachstate")
	}

	// 在创建期间禁用信号，这样新线程在禁用信号的情况下启动
	// 。这将使他们在minit。
	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
	var ret int32
	for tries := 0; tries < 20; tries++ {
		// pthread_create可以在EAGAIN中毫无原因地失败
		// 但是如果重试，它就可以了。
		ret = pthread_create(&tid, &attr, &tstart, unsafe.Pointer(mp))
		if ret != _EAGAIN {
			break
		}
		usleep(uint32(tries+1) * 1000) // 毫秒。
	}
	sigprocmask(_SIG_SETMASK, &oset, nil)
	if ret != 0 {
		print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", ret, ")\n")
		if ret == _EAGAIN {
			println("runtime: may need to increase max user processes (ulimit -u)")
		}
		throw("newosproc")
	}

}

func exitThread(wait *uint32) {
	// 我们不应该在AIX上访问exitThread，因为我们让
	// libc清理线程。
	throw("exitThread")
}

var urandom_dev = []byte("/dev/urandom\x00")

// go:nosplit 
func getRandomData(r []byte) {
	fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
	n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
	closefd(fd)
	extendRandom(r, int(n))
}

func goenvs() {
	goenvs_unix()
}

/* SIGNAL */

const (
	_NSIG = 256
)

// sigtranp是程序集
var sigtramp funcDescriptor

// go:nosplit 
// go:nowritebarrierec 
func setsig(i uint32, fn uintptr) {
	var sa sigactiont
	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
	sa.sa_mask = sigset_all
	if fn == abi.FuncPCABIInternal(sighandler) { // abi。FuncPCABIInternal（sighandler）匹配signal_unix中的调用者。go-
		fn = uintptr(unsafe.Pointer(&sigtramp))
	}
	sa.sa_handler = fn
	sigaction(uintptr(i), &sa, nil)

}

// go:nosplit-
// go:nowritebarrierrec-
func setsigstack(i uint32) {
	var sa sigactiont
	sigaction(uintptr(i), nil, &sa)
	if sa.sa_flags&_SA_ONSTACK != 0 {
		return
	}
	sa.sa_flags |= _SA_ONSTACK
	sigaction(uintptr(i), &sa, nil)
}

// go:nosplit-
// go:nowritebarrierrec-
func getsig(i uint32) uintptr {
	var sa sigactiont
	sigaction(uintptr(i), nil, &sa)
	return sa.sa_handler
}

// setSignaltstackSP设置堆栈的ss_sp字段。
// go:nosplit 
func setSignalstackSP(s *stackt, sp uintptr) {
	*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
}

// go:nosplit 
func (c *sigctxt) fixsigcode(sig uint32) {
	switch sig {
	case _SIGPIPE:
		// 对于SIGPIPE，c.sigcode（）在Linux上没有设置为_SI_USER。
		// 因此，如果
		// 它是在非执行线程中交付的，raisebadsignal将不会再次引发SIGPPIPE。
		c.set_sigcode(_SI_USER)
	}
}

// go:nosplit 
// go:NOWRITEBRIERREC 
func sigaddset(mask *sigset, i int) {
	(*mask)[(i-1)/64] |= 1 << ((uint32(i) - 1) & 63)
}

func sigdelset(mask *sigset, i int) {
	(*mask)[(i-1)/64] &^= 1 << ((uint32(i) - 1) & 63)
}

func setProcessCPUProfiler(hz int32) {
	setProcessCPUProfilerTimer(hz)
}

func setThreadCPUProfiler(hz int32) {
	setThreadCPUProfilerHz(hz)
}

// go:nosplit 
func validSIGPROF(mp *m, c *sigctxt) bool {
	return true
}

const (
	_CLOCK_REALTIME  = 9
	_CLOCK_MONOTONIC = 10
)

// go:nosplit 
func nanotime1() int64 {
	tp := &timespec{}
	if clock_gettime(_CLOCK_REALTIME, tp) != 0 {
		throw("syscall clock_gettime failed")
	}
	return tp.tv_sec*1000000000 + tp.tv_nsec
}

func walltime() (sec int64, nsec int32) {
	ts := &timespec{}
	if clock_gettime(_CLOCK_REALTIME, ts) != 0 {
		throw("syscall clock_gettime failed")
	}
	return ts.tv_sec, int32(ts.tv_nsec)
}

// go nosplit 
func fcntl(fd, cmd, arg int32) int32 {
	r, _ := syscall3(&libc_fcntl, uintptr(fd), uintptr(cmd), uintptr(arg))
	return int32(r)
}

// go nosplit 
func closeonexec(fd int32) {
	fcntl(fd, _F_SETFD, _FD_CLOEXEC)
}

// go nosplit 
func setNonblock(fd int32) {
	flags := fcntl(fd, _F_GETFL, 0)
	fcntl(fd, _F_SETFL, flags|_O_NONBLOCK)
}

// SIGPERTHEAD系统调用仅在linux上使用，因此我们分配了一个伪信号
// 。
const sigPerThreadSyscall = 1 << 31

// go:nosplit 
func runPerThreadSyscall() {
	throw("runPerThreadSyscall only valid on linux")
}
